﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace LexAnalysis
{
    /// <summary>
    /// 词法分析
    /// </summary>
    public class LexAnalysis:ILexAnalysis.ILexAnalysis
    {
        //Cooking源代码
        private string _sourceCode;
        //是否成功通过词法分析
        private bool _success;
        //当前单词符号
        private string _token;
        //种别编码
        private int _sym;
        //关键字列表
        private static readonly string[] KeyWords = {
                                                        "USING", "NEED", "NUMBER", "BEGIN", "END", "WHILE", "ENDWHILE",
                                                        "IF", "ENDIF",
                                                        "FUNCTION", "ENDFUNCTION", "AND", "OR", "NOT"
                                                    };
        //前一个单词是否为数值、标识符、闭括号
        private bool _afterNumber;
        //源程序游标
        private int _p;
        //读出来的下一个字符
        private char _ch;
        //行号
        private int _line;

        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="code">Cooking源代码</param>
        public LexAnalysis(string code)
        {
            _sourceCode = code;
            _success = true;
            _sym = -1;
            _afterNumber = false;
            _p = 0;
            _line = 1;
            //预处理
            Preprocessing();
        }

        /// <summary>
        /// 获取下一个单词符号
        /// </summary>
        /// <returns>下一个单词符号</returns>
        public string GetNextToken()
        {
            if (_p == _sourceCode.Length)//TODO 下边SCANNER的内容可能会导致数组越界，需要修改完善
            {
                _sym = -2;
            }
            else
            {
                //扫描
                Scanner();
            }
            return _token;
        }

        /// <summary>
        /// 获取当前单词符号的SYN
        /// </summary>
        /// <returns>当前单词符号的SYN</returns>
        public int GetSym()
        {
            return _sym;
        }


        /// <summary>
        /// 词法分析是否成功
        /// </summary>
        /// <returns>是否成功</returns>
        public bool Success()
        {
            return _success;
        }

        /// <summary>
        /// 获取当前行号
        /// </summary>
        /// <returns>当前行号</returns>
        public int GetLine()
        {
            return _line;
        }

        //编译预处理
        private void Preprocessing()
        {
            //去除注释
            _sourceCode = Regex.Replace(_sourceCode, ";.*\\n", "\\n");
            //连续的空格只保留一个空格
            _sourceCode = Regex.Replace(_sourceCode, "[ |\\t]{1,}", " ");
            //去除回车前后的空格
            _sourceCode = _sourceCode.Replace(" \n ", "\n");
            //全部字母转换为大写
            _sourceCode = _sourceCode.ToUpper();
            //TODO 读出来的文件编码格式的问题  以及回车是\n还是\r\n的问题  留待测试
        }

        //扫描源程序，识别一个单词符号，并且确定其种别编码
        private void Scanner()
        {
            _token = "";
            _ch = _sourceCode[_p++];
            //忽略空格
            while (_ch == ' ' || _ch == '\t')
            {
                _ch = _sourceCode[_p++];
            }
            //识别标识符和关键字
            if (_ch >= 'a' && _ch <= 'z' || _ch >= 'A' && _ch <= 'Z' || _ch == '_')
            {
                RecognizeIndentifier();
            }
            //数字
            else if ((_ch == '+' || _ch == '-') && !_afterNumber && _sourceCode[_p] >= '0' &&
                     _sourceCode[_p] <= '0' ||//如果不是在数值的后边，并且以+-开头且后边紧跟数值，则为正负号，属于数值
                     _ch >= '0' && _ch <= '9')//数值开头，为数值
            {
                RecognizeNumber();
            }
            else
            {
                _afterNumber = false;
                switch (_ch)
                {
                    case '+':
                        _sym = 30;
                        _token += _ch;
                        break;
                    case '-':
                        _sym = 31;
                        _token += _ch;
                        break;
                    case '*':
                        _sym = 32;
                        _token += _ch;
                        break;
                    case '/':
                        _sym = 33;
                        _token += _ch;
                        break;
                    case '=':
                        _token += _ch;
                        _ch = _sourceCode[_p++];
                        if (_ch == '=')
                        {
                            _sym = 44;
                            _token += _ch;
                        }
                        else
                        {
                            _sym = 34;
                            _p--;
                        }
                        break;
                    case '>':
                        _token += _ch;
                        _ch = _sourceCode[_p++];
                        if (_ch == '=')
                        {
                            _sym = 41;
                            _token += _ch;
                        }
                        else
                        {
                            _sym = 40;
                            _p--;
                        }
                        break;
                    case '<':
                        _token += _ch;
                        _ch = _sourceCode[_p++];
                        if (_ch == '=')
                        {
                            _sym = 43;
                            _token += _ch;
                        }
                        else
                        {
                            _sym = 42;
                            _p--;
                        }
                        break;
                    case '!':
                        _token += _ch;
                        _ch = _sourceCode[_p++];
                        if (_ch == '=')
                        {
                            _sym = 45;
                            _token += _ch;
                        }
                        else
                        {
                            _p--;
                        }
                        break;
                    case '\n':
                        _token += _ch;
                        _sym = 99;
                        _line++;
                        break;
                    case '\"':
                        _ch = _sourceCode[_p++];
                        while(_ch != '\"')
                        {
                            if(_ch == '\n')
                            {
                                _sym = -1;
                                _success = false;
                                Console.Error.WriteLine("Error:Should Exist a close \"... Line:{0}", _line);
                                break;
                            }
                            _token += _ch;
                            _ch = _sourceCode[_p++];
                        }
                        _sym = 22;
                        break;
                    case '(':
                        _sym = 50;
                        _token += _ch;
                        break;
                    case ')':
                        _sym = 51;
                        _token += _ch;
                        break;
                    default:
                        _sym = -1;
                        _success = false;
                        Console.Error.WriteLine("Error:Unexpecting character... Line:{0}", _line);
                        break;
                }
            }
        }

        //识别数字
        private void RecognizeNumber()
        {
            _sym = 21;
            _afterNumber = true;
            //确定数值的正负
            if(_ch == '+' || _ch == '-')
            {
                _token += _ch;
            }
            else
            {
                _token += '+';
                _token += _ch;
            }
            _ch = _sourceCode[_p++];
            //整数部分
            while(_ch >= '0' && _ch <= '9')
            {
                _token += _ch;
                _ch = _sourceCode[_p++];
            }
            //小数部分
            if(_ch == '.' && _sourceCode[_p] >= '0' && _sourceCode[_p] <= '9')
            {
                _ch = _sourceCode[_p++];
                while (_ch >= '0' && _ch <= '9')
                {
                    _token += _ch;
                    _ch = _sourceCode[_p++];
                }
            }
            _p--;
        }

        //识别标识符和关键字
        private void RecognizeIndentifier()
        {
            _token += _ch;
            _ch = _sourceCode[_p++];
            //获取标识符或者关键字单词
            while (_ch >= 'a' && _ch <= 'z' || _ch >= 'A' && _ch <= 'Z' || _ch >= '0' && _ch <= '9')
            {
                _token += _ch;
                _ch = _sourceCode[_p++];
            }
            _p--;
            _sym = 20;
            _afterNumber = true;
            //检查是否为关键字
            for(int i = 0; i<KeyWords.Length; i++)
            {
                if(_token == KeyWords[i])
                {
                    _sym = i;
                    _afterNumber = false;
                    break;
                }
            }
        }
    }
}
